package com.kaibes.core.base.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.github.junrar.Junrar;
import com.github.junrar.exception.RarException;

public class FileUtils {

	public final static int CREATE_MODEL_NEW = 1, CREATE_MODEL_CONTINUE = 2;
	private static byte[] ZIP_HEADER_1 = new byte[] { 80, 75, 3, 4 };
	private static byte[] ZIP_HEADER_2 = new byte[] { 80, 75, 5, 6 };
	private static byte[] RAR_HEADER_1 = new byte[] { 82, 97, 114, 33 };

	private static String getFileCode(File file, String name) {
		if (!file.isFile()) {
			return null;
		}
		try {
			FileInputStream in = new FileInputStream(file);
			byte buffer[] = new byte[8192];
			MessageDigest digest = MessageDigest.getInstance(name);
			int len;
			while ((len = in.read(buffer)) != -1) {
				digest.update(buffer, 0, len);
			}
			in.close();
			BigInteger bigInt = new BigInteger(1, digest.digest());
			return bigInt.toString(16);
		} catch (Exception e) {
			return null;
		}
	}

	public static String getCharset(String fileName) throws Exception {
		BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fileName));
		int p = (bin.read() << 8) + bin.read();
		bin.close();
		String code = null;

		switch (p) {
		case 0xefbb:
			code = "UTF-8";
			break;
		case 0xfffe:
			code = "Unicode";
			break;
		case 0xfeff:
			code = "UTF-16BE";
			break;
		default:
			code = "GBK";
		}

		return code;
	}

	public static byte[] getBytes(String path) {
		try {
			FileInputStream fis = new FileInputStream(path);
			byte[] bs = new byte[fis.available()];
			fis.read(bs);
			fis.close();
			return bs;
		} catch (IOException e) {
			return null;
		}
	}

	public static String getText(String path) {
		try {
			byte[] bs = getBytes(path);
			String content = new String(bs, getCharset(path));
			return content;
		} catch (Exception e) {
			return null;
		}
	}

	public static String getFileCode_(File file, String name) {
		if (!file.isFile()) {
			return null;
		}
		try {
			FileInputStream in = new FileInputStream(file);
			MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
			MessageDigest digest = MessageDigest.getInstance(name);
			digest.update(byteBuffer);
			in.close();
			BigInteger bigInt = new BigInteger(1, digest.digest());
			return bigInt.toString(16);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 计算文件md5值
	 * 
	 * @param file
	 * @return 返回文件md5值
	 */
	public static String getFileMD5(File file) {
		return getFileCode(file, "MD5");
	}

	/**
	 * 计算文件哈希值
	 * 
	 * @param file
	 * @return 返回文件哈希
	 */
	public static String getFileSha1(File file) {
		return getFileCode(file, "SHA-1");
	}

	/**
	 * 创建一个文件
	 * 
	 * @param filepath      文件路径
	 * @param filename      文件名
	 * @param downloadModel 创建模式，表示创建一个新文件,CREATE_MODEL_NEW，如果这个文件存在，则会变删除掉，CREATE_MODEL_CONTINUE,如果这个文件存在，则不做任何事
	 * @return 返回文件实例
	 * @throws IOException
	 */
	public static File createFile(String filepath, String filename, int downloadModel) throws IOException {
		File dir = new File(filepath);
		// 判断文件目录是否存在
		if (!dir.exists()) {
			dir.mkdirs();
		}

		File file = new File(dir, filename);
		if (downloadModel == FileUtils.CREATE_MODEL_NEW) {
			if (file.exists()) {
				file.delete();
				file.createNewFile();
			}
		} else {
			if (!file.exists()) {
				file.createNewFile();
			}
		}
		return file;
	}

	private final static Map<String, String> fileType = new HashMap<>();
	static {
		fileType.put("image/gif", ".gif");
		fileType.put("image/png", ".png");
	}

	public static String getSuffix(String type) {
		return fileType.get(type);
	}

	public static String getSuffix4Name(String filename) {
		int index = filename.lastIndexOf(".");
		if (index != -1) {
			return filename.substring(index);
		} else {
			return "";
		}
	}

	public static boolean delete(File file) {
		if (file.exists()) {
			if (file.isDirectory()) {
				File[] files = file.listFiles();
				for (File temp : files) {
					delete(temp);
				}
			}
			return file.delete();
		}
		return true;
	}

	private static Comparator<File> comparatorFile = new Comparator<File>() {

		@Override
		public int compare(File o1, File o2) {
			if (o1.getName().length() > o2.getName().length()) {
				return 1;
			} else if (o1.getName().length() < o2.getName().length()) {
				return -1;
			} else {
				return o1.getName().compareTo(o2.getName());
			}
		}
	};

	public static File[] sort(File[] files) {
		if (files == null) {
			return null;
		}
		if (files.length == 0) {
			return files;
		}
		List<File> fileList = Arrays.asList(files);
		Collections.sort(fileList, comparatorFile);
		fileList.toArray(files);
		return files;
	}

	public static boolean hasFile(String path) {
		File file = new File(path);
		if (file.exists()) {
			return file.isFile() || (file.isDirectory() && file.list().length > 0);
		}
		return false;
	}

	// 压缩文件相关
	private static boolean checkHeader(String inFilePath, byte[]... bs) {
		File file = new File(inFilePath);

		if (file.isDirectory()) {
			return false;
		}

		boolean isArchive = false;
		InputStream input = null;
		try {
			input = new FileInputStream(file);
			byte[] buffer = new byte[4];
			int length = input.read(buffer, 0, 4);
			if (length == 4) {
				for (int i = 0; i < bs.length; i++) {
					isArchive = isArchive || (Arrays.equals(bs[i], buffer));
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
				}
			}
		}

		return isArchive;
	}

	public static boolean isZipFile(String inFilePath) {
		return checkHeader(inFilePath, ZIP_HEADER_1, ZIP_HEADER_2);
	}

	public static boolean isRarFile(String inFilePath) {
		return checkHeader(inFilePath, RAR_HEADER_1);
	}

	public static boolean isArchiveFile(String inFilePath) {
		return checkHeader(inFilePath, ZIP_HEADER_1, ZIP_HEADER_2, RAR_HEADER_1);
	}

	public static void compress() {
	}

	public static void unCompress(String path, String out) {
		unCompress(path, out, Charset.defaultCharset());
	}

	public static void unCompress(String path, String out, Charset charset) {
		String filename = new File(path).getName();
		filename = filename.substring(0, filename.lastIndexOf("."));
		out = out + filename;
		File fout = new File(out);
		if (!fout.exists()) {
			fout.mkdirs();
		}
		if (fout.list().length == 0) {
			if (isZipFile(path)) {
				ZipUtils.unZipFile(path, out, charset);
			} else {
				try {
					Junrar.extract(path, out);
				} catch (IOException | RarException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static boolean transferTo(File in, OutputStream os) {
		return transferTo(in, os, 0L);
	}

	public static boolean transferTo(File in, OutputStream os, long position) {
		try {
			RandomAccessFile rfile = new RandomAccessFile(in, "r");
			WritableByteChannel write = Channels.newChannel(os);
			rfile.getChannel().transferTo(position, rfile.length(), write);
			write.close();
			rfile.close();
			return true;
		} catch (Exception e) {
			return false;
		}
	}
}
